/***********************************************************************************************************************
*                                                                                                                      *
* SPLASH build system v0.1                                                                                             *
*                                                                                                                      *
* Copyright (c) 2013 Andrew D. Zonenberg                                                                               *
* All rights reserved.                                                                                                 *
*                                                                                                                      *
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the     *
* following conditions are met:                                                                                        *
*                                                                                                                      *
*    * Redistributions of source code must retain the above copyright notice, this list of conditions, and the         *
*      following disclaimer.                                                                                           *
*                                                                                                                      *
*    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the       *
*      following disclaimer in the documentation and/or other materials provided with the distribution.                *
*                                                                                                                      *
*    * Neither the name of the author nor the names of any contributors may be used to endorse or promote products     *
*      derived from this software without specific prior written permission.                                           *
*                                                                                                                      *
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED   *
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL *
* THE AUTHORS BE HELD LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES        *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR       *
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE       *
* POSSIBILITY OF SUCH DAMAGE.                                                                                          *
*                                                                                                                      *
***********************************************************************************************************************/

#include "splashcore.h"
#include <unordered_set>

using namespace std;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Construction / destruction

/**
	@brief Initializes the default toolchain
 */
GnuCppToolchain::GnuCppToolchain()
{
	//Get the default Debian architecture
	//TODO: Run on non-Debian
	string arch = ShellCommand("dpkg-architecture | grep DEB_HOST_MULTIARCH | cut -d'=' -f2", true);
	DebugPrintfHeading(3, "Host architecture seems to be %s\n", arch.c_str());
	
	//Get the default compiler path
	char cmd[64];
	snprintf(cmd, sizeof(cmd), "which %s-g++", arch.c_str());
	m_gxxpath = ShellCommand(cmd, true);
	if(m_gxxpath == "")
		FatalError("No candidate path found for GNU C++ compiler\n");
	DebugPrintfHeading(3, "Found candidate path for GNU C++ compiler\n");
	DebugPrintfSilent(3, "    %s\n", m_gxxpath.c_str());
	m_gxxpath = CanonicalizePath(m_gxxpath);
	DebugPrintfSilent(3, "    => %s\n", m_gxxpath.c_str());

	//Get the full version number
	m_major = -1;
	m_minor = -1;
	m_patch = -1;
	string ver = ShellCommand(m_gxxpath + " --version | head -n 1", true);
	sscanf(ver.c_str(), "%*s (%*[^)]) %d.%d.%d", &m_major, &m_minor, &m_patch);
	
	m_arch = ShellCommand(m_gxxpath + " -dumpmachine", true);
	
	m_arpath = CanonicalizePath(ShellCommand("which ar", true));
	m_objcopypath = CanonicalizePath(ShellCommand("which objcopy", true));
	
	m_gccpath = str_replace("g++", "gcc", m_gxxpath);
	DebugPrintfHeading(2, "Found candidate path for GNU C compiler\n");
	DebugPrintfSilent(2, "    %s\n", m_gccpath.c_str());
	
	m_cppcheckpath = ShellCommand("which cppcheck", true);
	if(m_cppcheckpath != "")
	{	
		DebugPrintfHeading(2, "Found candidate path for cppcheck\n");
		m_cppcheckpath = CanonicalizePath(m_cppcheckpath);
		DebugPrintfSilent(2, "    %s\n", m_cppcheckpath.c_str());
	}
	else
		DebugPrintfSilent(0, "WARNING: no cppcheck found\n");
}

/**
	@brief Initializes the default toolchain for a given architecture
 */
GnuCppToolchain::GnuCppToolchain(string arch)
{
	//Get the default compiler path
	char cmd[64];
	snprintf(cmd, sizeof(cmd), "which %s-g++", arch.c_str());
	m_gxxpath = ShellCommand(cmd, true);
	
	//If not found, try guessing with version
	if(m_gxxpath.empty())
	{
		//Get the version of the host compiler and guess it's the same
		int major, minor, patch;
		string ver = ShellCommand("g++ --version | head -n 1", true);
		sscanf(ver.c_str(), "%*s (%*[^)]) %d.%d.%d", &major, &minor, &patch);
		snprintf(cmd, sizeof(cmd), "which %s-g++-%d.%d", arch.c_str(), major, minor);
		m_gxxpath = ShellCommand(cmd, true);
	}
	DebugPrintfHeading(2, "Found candidate path for GNU C++ compiler\n");
	DebugPrintfSilent(2, "    %s\n", m_gxxpath.c_str());
	if(m_gxxpath.empty())
		FatalError("Couldn't find g++ for %s\n", arch.c_str());

	m_gxxpath = CanonicalizePath(m_gxxpath);
	DebugPrintfSilent(2, "    => %s\n", m_gxxpath.c_str());

	//Get the full version number
	m_major = -1;
	m_minor = -1;
	m_patch = -1;
	string ver = ShellCommand(m_gxxpath + " --version | head -n 1", true);
	sscanf(ver.c_str(), "%*s (%*[^)]) %d.%d.%d", &m_major, &m_minor, &m_patch);
	
	m_arch = ShellCommand(m_gxxpath + " -dumpmachine", true);
	
	//Special check for host arch
	if(arch == ShellCommand("g++ -dumpmachine", true))
	{
		m_arpath = CanonicalizePath(ShellCommand("which ar", true));
		m_objcopypath = CanonicalizePath(ShellCommand("which objcopy", true));
	}
	else
	{
		snprintf(cmd, sizeof(cmd), "which %s-ar", arch.c_str());
		m_arpath = CanonicalizePath(ShellCommand(cmd, true));
		snprintf(cmd, sizeof(cmd), "which %s-objcopy", arch.c_str());
		m_objcopypath = CanonicalizePath(ShellCommand(cmd, true));
	}
	
	m_gccpath = str_replace("g++", "gcc", m_gxxpath);
	DebugPrintfHeading(2, "Found candidate path for GNU C compiler\n");
	DebugPrintfSilent(2, "    %s\n", m_gccpath.c_str());
	
	m_cppcheckpath = ShellCommand("which cppcheck", true);
	if(m_cppcheckpath != "")
	{	
		DebugPrintfHeading(2, "Found candidate path for cppcheck\n");
		m_cppcheckpath = CanonicalizePath(m_cppcheckpath);
		DebugPrintfSilent(2, "    %s\n", m_cppcheckpath.c_str());
	}
	else
		DebugPrintfSilent(0, "WARNING: no cppcheck found\n");
}

GnuCppToolchain::GnuCppToolchain(string arch, int major, int minor, int /*patch*/)
{
	char cmd[64];
	snprintf(cmd, sizeof(cmd), "which %s-g++-%d.%d", arch.c_str(), major, minor);
	m_gxxpath = ShellCommand(cmd, true);
	
	//Get the full version number
	m_major = -1;
	m_minor = -1;
	m_patch = -1;
	string ver = ShellCommand(m_gxxpath + " --version | head -n 1", true);
	sscanf(ver.c_str(), "%*s (%*[^)]) %d.%d.%d", &m_major, &m_minor, &m_patch);
	
	m_arch = ShellCommand(m_gxxpath + " -dumpmachine", true);
	
	//Special check for host arch
	if(arch == ShellCommand("g++ -dumpmachine", true))
	{
		m_arpath = CanonicalizePath(ShellCommand("which ar", true));
		m_objcopypath = CanonicalizePath(ShellCommand("which objcopy", true));
	}
	else
	{
		snprintf(cmd, sizeof(cmd), "which %s-ar", arch.c_str());
		m_arpath = CanonicalizePath(ShellCommand(cmd, true));
		snprintf(cmd, sizeof(cmd), "which %s-objcopy", arch.c_str());
		m_objcopypath = CanonicalizePath(ShellCommand(cmd, true));
	}
	
	m_gccpath = str_replace("g++", "gcc", m_gxxpath);
	DebugPrintfHeading(2, "Found candidate path for GNU C compiler\n");
	DebugPrintfSilent(2, "    %s\n", m_gccpath.c_str());
	
	m_cppcheckpath = ShellCommand("which cppcheck", true);
	if(m_cppcheckpath != "")
	{	
		DebugPrintfHeading(2, "Found candidate path for cppcheck\n");
		m_cppcheckpath = CanonicalizePath(m_cppcheckpath);
		DebugPrintfSilent(2, "    %s\n", m_cppcheckpath.c_str());
	}
	else
		DebugPrintfSilent(0, "WARNING: no cppcheck found\n");
}

GnuCppToolchain::~GnuCppToolchain()
{
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Accessors

string GnuCppToolchain::GetArchitecture() const
{
	return m_arch;
}

bool GnuCppToolchain::SanityCheck() const
{
	if(!DoesFileExist(m_gxxpath))
		return false;
	if(!DoesFileExist(m_gccpath))
		return false;
	if(!DoesFileExist(m_arpath))
		return false;		
	return true;
}

string GnuCppToolchain::GetDescription() const
{
	//TODO: cache
	string ver = ShellCommand(m_gxxpath + " --version | head -n 1", true);
	return ver + " " + m_arch; 
}

string GnuCppToolchain::GetHash() const
{
	//TODO: cache
	return sha256(GetDescription());
}

string GnuCppToolchain::GetCflagsHash(const CppCompileFlagList& flags) const
{
	string sflags;
	for(size_t i=0; i<flags.size(); i++)
		sflags += flags[i]->toString(this) + " ";
	return sha256(sflags);
}

string GnuCppToolchain::GetLflagsHash(const CppLinkFlagList& flags) const
{
	string sflags;
	for(size_t i=0; i<flags.size(); i++)
	{
		//Special parsing for linker scripts
		const CppLinkerScriptFlag* plflag = dynamic_cast<const CppLinkerScriptFlag*>(flags[i]);
		if(plflag != NULL)
			sflags += sha256_file(plflag->GetName());
		
		//In either case, add the hash of the string itself
		sflags += flags[i]->toString(this) + " ";
	}
	return sha256(sflags);
}

string GnuCppToolchain::GetExecutableSuffix() const
{
	if(IsWindowsToolchain())
		return ".exe";
	else
		return "";
}

string GnuCppToolchain::GetStaticLibrarySuffix() const
{
	/*if(IsWindowsToolchain())
		return ".lib";
	else*/
		return ".a";
}

string GnuCppToolchain::GetSharedLibrarySuffix() const
{
	/*if(IsWindowsToolchain())
		return ".dll";
	else*/
		return ".so";
}

bool GnuCppToolchain::IsWindowsToolchain() const
{
	return (m_arch.find("mingw") != string::npos);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Include path resolution

void GnuCppToolchain::GetCompilerIncludePaths(vector<string>& paths)
{
	char sver_full[32];
	char sver[32];
	snprintf(sver_full, sizeof(sver_full), "%d.%d.%d", m_major, m_minor, m_patch);
	snprintf(sver, sizeof(sver), "%d.%d", m_major, m_minor);

	//Compiler can use either maj.min (mingw) or maj.min.patch (emdebian) paths
	//Go figure out which they're using
	string basepath = string("/usr/lib/gcc/") + m_arch + "/" + sver_full;
	if(!DoesDirectoryExist(basepath))
		basepath = string("/usr/lib/gcc/") + m_arch + "/" + sver;
	
	paths.push_back(CanonicalizePath(basepath + "/include"));
	paths.push_back(CanonicalizePath(basepath + "/include-fixed"));
	
	string compiler_base = string("/usr/include/c++/") + sver_full;
	string apath;
	if(DoesDirectoryExist(compiler_base))
	{
		paths.push_back(CanonicalizePath(compiler_base));
		apath = string("/usr/include/c++/") + sver_full + "/" + m_arch;
		if(DoesDirectoryExist(apath))
			paths.push_back(CanonicalizePath(apath));
	}
	apath = string("/usr/") + m_arch + "/include";
	if(DoesDirectoryExist(apath))
		paths.push_back(CanonicalizePath(apath));
}

void GnuCppToolchain::GetDefaultMacros(const CppCompileFlagList& /*flags*/, map<string, string>& macros)
{
	char sver[32];
	snprintf(sver, sizeof(sver), "%d", m_major);
	macros["__GNUC__"] = sver;
	snprintf(sver, sizeof(sver), "%d", m_minor);
	macros["__GNUC_MINOR__"] = sver;
	macros["__GNUC_PATCHLEVEL__"] = "0";	//TODO
	
	macros["__GNUG__"] = "1";				//C++ mode
	
	macros["_GNU_SOURCE"] = "1";
	
	macros["__STDC_VERSION__"] = "199901";	//C99
	
	//Windows support
	if(IsWindowsToolchain())
	{
		macros["_WIN32"] = "1";
		macros["_WINDOWS"] = "1";
	}
	
	//TODO: add macros in cflags once we support that
	
	//Don't worry about all of the fancy stuff, we just need to get include paths
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Toolchain creation

CppToolchain* GnuCppToolchain::CreateDefaultToolchain()
{
	//Use the default "g++" toolchain
	GnuCppToolchain* toolchain = new GnuCppToolchain();
	if(toolchain->SanityCheck())
		return toolchain;
		
	//give up
	delete toolchain;
	return NULL;
}

CppToolchain* GnuCppToolchain::CreateDefaultToolchainForArchitecture(string arch)
{
	//Use the default "g++" toolchain for the architecture
	GnuCppToolchain* toolchain = new GnuCppToolchain(arch);
	if(toolchain->SanityCheck())
		return toolchain;
		
	//give up
	delete toolchain;
	return NULL;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Compilation

ClusterJob* GnuCppToolchain::CompileSourceToObject(
	string source_filename,
	string object_filename,
	const CppCompileFlagList& flags,
	const vector<ClusterJob*>& deps,
	Cluster* cluster,
	string project_root)
{
	//Print the heading
	DebugPrintfHeading(3, "Compiling source file %s\n", GetRelativePathOfFile(project_root, source_filename).c_str());
	DebugPrintfSilent (5, "    with g++ %d.%d.%d\n", m_major, m_minor, m_patch);
	
	//Get output directory
	string path = GetDirOfFile(object_filename);
	string base = GetBasenameOfFileWithoutExt(object_filename);
		
	//Format flags
	string sflags;
	for(size_t i=0; i<flags.size(); i++)
		sflags += flags[i]->toString(this) + " ";
	
	//Do not use "small data" / gp optimizations for now.
	if(m_arch.find("mips") != string::npos)
		sflags += "-mno-gpopt -G0 ";
		
	//Include all expected default include paths (libgcc etc) explicitly
	vector<string> paths;
	GetCompilerIncludePaths(paths);
	for(auto path : paths)
		sflags += "-I " + path + " ";
	
	//Add defines if on Windows
	if(IsWindowsToolchain())
		sflags += "-D_WIN32 -D_WINDOWS ";
		
	string extra_warnings = "-Wall -Wextra -Wcast-align -Winit-self -Wmissing-declarations -Wswitch -Wwrite-strings";
	
	//Check for special language suffixes
	//if not found, default to C++
	bool lang_is_asm = (source_filename.find(".S") != string::npos);
	bool lang_is_c = (source_filename.find(".c") != string::npos) && (source_filename.find(".cpp") == string::npos);
		
	//Create the batch script
	string batch_fname = path + "/" + base + "_g++.sh";
	FILE* fp = fopen(batch_fname.c_str(), "w");
	if(!fp)
		FatalError("Failed to create batch script %s\n", batch_fname.c_str());
	fprintf(fp, "#!/bin/bash\n");
	if( lang_is_asm || lang_is_c )
	{
		//do not force c++ for .S or .c
		fprintf(fp, "%s %s -c %s -o %s %s\n",
			m_gccpath.c_str(),
			sflags.c_str(),
			source_filename.c_str(),
			object_filename.c_str(),
			extra_warnings.c_str());
	}
	else
	{
		//otherwise force to c++ so we can handle weird names like Splashfile
		fprintf(fp, "%s -x c++ %s -c %s -o %s %s\n",
			m_gxxpath.c_str(),
			sflags.c_str(),
			source_filename.c_str(),
			object_filename.c_str(),
			extra_warnings.c_str());
	}
	fprintf(fp, "if [ $? -ne 0 ]; then\n");
	fprintf(fp, "    touch build_failed\n");
	fprintf(fp, "    exit 1\n");
	fprintf(fp, "else\n");
	fprintf(fp, "    touch build_successful\n");
	fprintf(fp, "fi\n");
	
	//Run cppcheck if we have it - but not on Splashfiles since we'll get bogus errors
	if( (m_cppcheckpath != "") && (source_filename.find("Splashfile") == string::npos) )
		fprintf(fp, "%s --enable=style --enable=performance --enable=portability %s > /dev/null", m_cppcheckpath.c_str(), source_filename.c_str());
	
	fclose(fp);
	if(0 != chmod(batch_fname.c_str(), 0755))
		FatalError("Failed to set execute permissions on batch script %s\n", batch_fname.c_str());
		
	//Submit the batch script
	return cluster->SubmitBatchJob(batch_fname, cluster->m_cppBuildPartition, path, deps);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Linking

ClusterJob* GnuCppToolchain::LinkObjectsToExecutable(
		const vector<string>& objects,
		string exe_filename,
		const CppLinkFlagList& flags,
		const vector<ClusterJob*>& deps,
		Cluster* cluster
		)
{
	//Get output directory
	string path = GetDirOfFile(exe_filename);
	string base = GetBasenameOfFile(exe_filename);
	string basewe = GetBasenameOfFileWithoutExt(exe_filename);
	
	//TODO: This is hacky, do something better
	string archpath = CanonicalizePath(path + "/../../" + GetArchitecture());
	
	//Print the heading
	DebugPrintfHeading(3, "Linking executable file %s\n", base.c_str());
	DebugPrintfSilent (5, "    with g++ %d.%d.%d\n", m_major, m_minor, m_patch);
	
	//Format flags
	string sflags;
	for(size_t i=0; i<flags.size(); i++)
		sflags += string("\\\n    ") + flags[i]->toString(this) + " ";
	
	//Do not use "small data" / gp optimizations
	if(m_arch.find("mips") != string::npos)
		sflags += "-mno-gpopt -G0 ";
	
	//Create the batch script
	string batch_fname = path + "/" + basewe + "_ld.sh";
	FILE* fp = fopen(batch_fname.c_str(), "w");
	if(!fp)
		FatalError("Failed to create batch script %s\n", batch_fname.c_str());
	fprintf(fp, "#!/bin/bash\n");
	fprintf(fp, "%s \\\n    -o %s -L%s",
		m_gxxpath.c_str(),
		exe_filename.c_str(),
		archpath.c_str()
		);
	for(size_t i=0; i<objects.size(); i++)
		fprintf(fp, " \\\n    %s ", objects[i].c_str());
	fprintf(fp, " \\\n    %s", sflags.c_str());			//static libraries need to be after sources
	fprintf(fp, "\n");
	fprintf(fp, "if [ $? -ne 0 ]; then\n");
	fprintf(fp, "    touch build_failed\n");
	fprintf(fp, "    exit 1\n");
	fprintf(fp, "else\n");
	fprintf(fp, "    touch build_successful\n");
	fprintf(fp, "fi\n");
	fclose(fp);
	if(0 != chmod(batch_fname.c_str(), 0755))
		FatalError("Failed to set execute permissions on batch script %s\n", batch_fname.c_str());
		
	//Submit the batch script
	return cluster->SubmitBatchJob(batch_fname, cluster->m_cppBuildPartition, path, deps);
}

ClusterJob* GnuCppToolchain::LinkObjectsToSharedLibrary(
	const vector<string>& objects,
	string exe_filename,
	const CppLinkFlagList& flags,
	const vector<ClusterJob*>& deps,
	Cluster* cluster
	)
{
	//Get output directory
	string path = GetDirOfFile(exe_filename);
	string base = GetBasenameOfFile(exe_filename);
	string basewe = GetBasenameOfFileWithoutExt(exe_filename);
	
	//TODO: This is hacky, do something better
	string archpath = CanonicalizePath(path + "/../../" + GetArchitecture());
	
	//Print the heading
	DebugPrintfHeading(3, "Linking shared object file %s\n", base.c_str());
	DebugPrintfSilent (5, "    with g++ %d.%d.%d\n", m_major, m_minor, m_patch);
	
	//Format flags
	string sflags;
	for(size_t i=0; i<flags.size(); i++)
		sflags += string("\\\n    ") + flags[i]->toString(this) + " ";

	//Do not use "small data" / gp optimizations
	if(m_arch.find("mips") != string::npos)
		sflags += "-mno-gpopt -G0 ";

	//Create the batch script
	string batch_fname = path + "/ld_batch_command_" + basewe + ".sh";
	FILE* fp = fopen(batch_fname.c_str(), "w");
	if(!fp)
		FatalError("Failed to create batch script %s\n", batch_fname.c_str());
	fprintf(fp, "#!/bin/bash\n");
	fprintf(fp, "%s \\\n    -o %s\\\n    -shared -L%s",
		m_gxxpath.c_str(),
		exe_filename.c_str(),
		archpath.c_str()
		);		
	for(size_t i=0; i<objects.size(); i++)
		fprintf(fp, " \\\n    %s ", objects[i].c_str());
	fprintf(fp, " \\\n    %s", sflags.c_str());			//static libraries need to be after sources
	fprintf(fp, "\n");
	fprintf(fp, "if [ $? -ne 0 ]; then\n");
	fprintf(fp, "    touch build_failed\n");
	fprintf(fp, "    exit 1\n");
	fprintf(fp, "else\n");
	fprintf(fp, "    touch build_successful\n");
	fprintf(fp, "fi\n");
	fclose(fp);
	if(0 != chmod(batch_fname.c_str(), 0755))
		FatalError("Failed to set execute permissions on batch script %s\n", batch_fname.c_str());
		
	//Submit the batch script
	return cluster->SubmitBatchJob(batch_fname, cluster->m_cppBuildPartition, path, deps);
}

ClusterJob* GnuCppToolchain::LinkObjectsToStaticLibrary(
	const vector<string>& objects,
	string exe_filename,
	const CppLinkFlagList& flags,
	const vector<ClusterJob*>& deps,
	Cluster* cluster
	)
{
	//Get output directory
	string path = GetDirOfFile(exe_filename);
	string base = GetBasenameOfFile(exe_filename);
	string basewe = GetBasenameOfFileWithoutExt(exe_filename);
	
	//Print the heading
	DebugPrintfHeading(3, "Linking static library file %s\n", base.c_str());
	DebugPrintfSilent (5, "    with g++ %d.%d.%d\n", m_major, m_minor, m_patch);
	
	//Format flags
	if(flags.size() != 0)
		FatalError("ar flags not supported");

	//Create the batch script
	string batch_fname = path + "/ar_batch_command_" + basewe + ".sh";
	FILE* fp = fopen(batch_fname.c_str(), "w");
	if(!fp)
		FatalError("Failed to create batch script %s\n", batch_fname.c_str());
	fprintf(fp, "#!/bin/bash\n");
	fprintf(fp, "%s rcs\\\n    -o %s",
		m_arpath.c_str(),
		exe_filename.c_str()
		);		
	for(size_t i=0; i<objects.size(); i++)
		fprintf(fp, " \\\n    %s ", objects[i].c_str());
	fprintf(fp, "\n");
	fprintf(fp, "if [ $? -ne 0 ]; then\n");
	fprintf(fp, "    touch build_failed\n");
	fprintf(fp, "    exit 1\n");
	fprintf(fp, "else\n");
	fprintf(fp, "    touch build_successful\n");
	fprintf(fp, "fi\n");
	fclose(fp);
	if(0 != chmod(batch_fname.c_str(), 0755))
		FatalError("Failed to set execute permissions on batch script %s\n", batch_fname.c_str());
		
	//Submit the batch script
	return cluster->SubmitBatchJob(batch_fname, cluster->m_cppBuildPartition, path, deps);
}

ClusterJob* GnuCppToolchain::DumpSectionsFromExecutable(
		string exe_filename,
		string bin_filename,
		vector<string> sections,
		const vector<ClusterJob*>& deps,
		Cluster* cluster
		)
{
	//Get output directory
	string path = GetDirOfFile(exe_filename);
	string base = GetBasenameOfFile(exe_filename);
	string basewe = GetBasenameOfFileWithoutExt(exe_filename);
	
	//Print the heading
	DebugPrintfHeading(3, "Generating binary firmware file %s\n", base.c_str());

	//Create the batch script
	string batch_fname = path + "/objcopy_batch_command_" + basewe + ".sh";
	FILE* fp = fopen(batch_fname.c_str(), "w");
	if(!fp)
		FatalError("Failed to create batch script %s\n", batch_fname.c_str());
	fprintf(fp, "#!/bin/bash\n");
	fprintf(fp, "%s -O binary \\\n    %s\\\n    %s",
		m_objcopypath.c_str(),
		exe_filename.c_str(),
		bin_filename.c_str()
		);		
	for(size_t i=0; i<sections.size(); i++)
		fprintf(fp, " \\\n    -j %s ", sections[i].c_str());
	fprintf(fp, "\n");
	fprintf(fp, "if [ $? -ne 0 ]; then\n");
	fprintf(fp, "    touch build_failed\n");
	fprintf(fp, "    exit 1\n");
	fprintf(fp, "else\n");
	fprintf(fp, "    touch build_successful\n");
	fprintf(fp, "fi\n");
	fclose(fp);
	if(0 != chmod(batch_fname.c_str(), 0755))
		FatalError("Failed to set execute permissions on batch script %s\n", batch_fname.c_str());
		
	//Submit the batch script
	return cluster->SubmitBatchJob(batch_fname, cluster->m_cppBuildPartition, path, deps);
}
